package flib;

import importExport.SVGText;

import java.io.File;
import java.util.Collections;
import java.util.Vector;

import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PFont;
import simpleGeom.Bounds3D;
import simpleGeom.Edge;

import simpleGeom.HasPos2D;
import simpleGeom.HasPos3D;
import simpleGeom.Rect2D;
import simpleGeom.Vec2D;
import simpleGeom.Vec3D;

public class SplitApplet extends PApplet {
	Plan plan;
	PFont myFont;

	Vector<Split> splits;

	SplitArea root;

	Room roomRoot;

	Room roomRoot2;
	
	boolean doReset;
	PeasyCam cam;
	int[][]connections;
	Rect2D[]bounds;
	String input;
	public void setup() {
		size(1024, 768);
		myFont = createFont("FFScala", 16f);
		textFont(myFont,1);
		FloorplanLoader2D loader = new FloorplanLoader2D();
		loader.loadWallTypes(new File("../flibConfig.xml"));
		loader.loadRoomTypes(new File("../flibConfig.xml"));
		plan = loader.loadFloorplanSVG(new File(input));
		connections=new int[plan.rooms.size()][plan.rooms.size()];
		bounds=new Rect2D[plan.rooms.size()];
		for (int i = 0; i < plan.rooms.size(); i++) {
			Room room = plan.rooms.get(i);
			room.id=i;
			bounds[i]=room.getBounds2D();
			println(room.getHEdges().size());
		}
		findSplits();
		collectAxis();
		
		for (int i = 0; i < plan.nodes.size(); i++) {
			FlibHENode p = plan.nodes.get(i);
			p.setZ(0);
			
			NodeData nD = new NodeData();
			p.setData(nD);
		}
		
		for (int i = 0; i < plan.walls.size(); i++) {
			FlibWall p = plan.walls.get(i);
			Room r1=p.getR1();
			Room r2=p.getR2();
			if (r1!=null&&r2!=null){
				//println(p.type.label);
				if (p.type.label.equals("wall-help")){
					
					connections[r1.id][r2.id]=1;
					connections[r2.id][r1.id]=1;
				}
			}
		}
		collectSplitFaces();
		Vector<Room> splitLeaves = new Vector<Room>();
		roomRoot.collectAllLeaves(splitLeaves);
		
		fill(255, 0, 255);

		for (int i = 0; i < splitLeaves.size(); i++) {
			Room sA = splitLeaves.get(i);
			for (int j=0;j<plan.rooms.size();j++){
				Room r=plan.rooms.get(j);
				println("overlap:" +sA.getBounds2D().getOverlapArea(r.getBounds2D()));
				if (sA.getBounds2D().getOverlapArea(r.getBounds2D())>10){
					sA.type=r.type;
					sA.id=j;
				}
			}
		}
	
		recreate();
		//cam=new PeasyCam(this,500);
	}

	public void recreate() {
		Vector<FlibHENode> newNodes = new Vector<FlibHENode>();
		for (int i = 0; i < roomRoot.segments.size(); i++) {
			EdgeSegment se = roomRoot.segments.get(i);
			FlibHENode cN = new FlibHENode(se.hEdge.getOrg());
			newNodes.add(cN);
			NodeData nD = new NodeData();
			nD.corner = true;
			cN.data = nD;
		}
		roomRoot2 = new Room();
		FlibHEdge hEdge = null;
		for (int i = 0; i < newNodes.size(); i++) {
			FlibHENode n1 = newNodes.get(i);
			FlibHENode n2 = newNodes.get((i + 1) % newNodes.size());
			hEdge = new FlibHEdge(n1, n2);
			hEdge.attach();
		}
		roomRoot2.setHEdge(hEdge);
		roomRoot2.initCornerPts();
		roomRoot2.setCornerPts();
		hEdge.setFaceToAll(roomRoot2);
		println("hEFace: "+hEdge.getSym().getFace());
		Vector<Room> roomsToSplit1 = new Vector<Room>();
		roomsToSplit1.add(roomRoot);
		Vector<Room> roomsToSplit2 = new Vector<Room>();
		roomsToSplit2.add(roomRoot2);
		int id = 0;
		while (roomsToSplit1.size() > 0) {
			id++;
			println("id: " + id);
			Vector<Room> newCandidates = new Vector<Room>();
			Vector<Room> newCandidates2 = new Vector<Room>();
			for (int i = 0; i < roomsToSplit1.size(); i++) {
				Room r = roomsToSplit1.get(i);

				Room r2 = roomsToSplit2.get(i);
				println("r2: " + r2.segments.size());
				if (r.children.size() > 0) {
					println(r.seg1 + "," + r.seg2 + "," + r.relPos1 + ","
							+ r.relPos2);
					Split split=r.split;
					println("split: "+split);
					FlibHENode sN1=split.hEdges.firstElement().getOrg();
					FlibHENode sN2=split.hEdges.lastElement().getDest();
					if(r.relPos1>0.0&&r.relPos1<1&&r.relPos2>0.0&&r.relPos2<1&&sN1.getNHEdges()<4&&sN2.getNHEdges()<4){
						float cR=random(-0.1f,0.1f);
						float newR=r.relPos1+cR;
						//cR=random(-0.1f,0.1f);
						float newR2=r.relPos2-cR;
						if (newR<=0)newR=0.2f;
						if (newR>=1)newR=0.8f;
						if (newR2<=0)newR2=0.2f;
						if (newR2>=1)newR2=0.8f;
						int newSeg=r.seg1;
						/*println("newR: "+newR);
						float newSeg=r.seg1+(int)random(-1f,2f);
						if (newSeg==r.seg2||newSeg<0){
							newSeg=r.seg1;
						}*/
						r2.split(newSeg, r.seg2, newR,newR2);
					}
					else{
						r2.split(r.seg1, r.seg2, r.relPos1, r.relPos2);
					}
					for (int j = 0; j < r.children.size(); j++) {
						//r2.children.get(j).setCornerPts();
						println("room:    "+r.children.get(j).segments.size());
						println("room2:   "+r2.children.get(j).segments.size());
						newCandidates.add(r.children.get(j));
						newCandidates2.add(r2.children.get(j));
					}
					
				}
			}
			roomsToSplit1 = newCandidates;
			roomsToSplit2 = newCandidates2;
		}
		Vector<Room> splitLeaves0 = new Vector<Room>();
		roomRoot.collectAllLeaves(splitLeaves0);
		
		Vector<Room> splitLeaves = new Vector<Room>();
		roomRoot2.collectAllLeaves(splitLeaves);
		for (int i = 0; i < splitLeaves.size(); i++) {
			Room sA = splitLeaves.get(i);
			sA.id=splitLeaves0.get(i).id;
			sA.getHEdge().setFaceToAll(sA);
		}
	}
	public void drawPlan(){
		stroke(255);
		for (int i = 0; i < plan.walls.size(); i++) {
			FlibWall p = plan.walls.get(i);
			Room r1=p.getR1();
			Room r2=p.getR2();
			if (r1!=null&&r2!=null){
				//println(p.type.label);
				stroke(255);

				if (p.type.label.equals("wall-help")){
					stroke(255,0,0);
				}
				p.display2D(g);
			}
		}
		fill(255);
		for (int i=0;i<bounds.length;i++){
			Rect2D r=bounds[i];
		text(""+i,r.getCenterX(),r.getCenterY());
		}
	}
	public void drawPlan(Room room){
		stroke(255);
		Vector<Room> splitLeaves = new Vector<Room>();
		room.collectAllLeaves(splitLeaves);
		
		stroke(255);
		strokeWeight(0.1f);
		//randomSeed(1);
		//noStroke();
		
		//randomSeed(1);
		
		for (int i = 0; i < splitLeaves.size(); i++) {
			Room sA = splitLeaves.get(i);
			
			if (frameCount==1)
			println(sA.id);
			//fill(random(255), random(255), random(255));
			//fill(sA.id*255f/bounds.length,255,255);
			//text(""+sA.id,sA.getBounds2D().getCenterX(),sA.getBounds2D().getCenterY());
			//sA.getBounds2D().display(g);
			FlibHEdge hE=sA.getHEdge();
			do{
				Room nb=(Room) hE.getSym().getFace();
				if (nb!=null&&sA!=null){
					if (connections[nb.id][sA.id]==0){
						hE.display(g);
					}
				}
				hE=hE.getNext();
			}while(hE!=sA.getHEdge());
		}
	}
	public void drawRest(){
		colorMode(HSB);
		rectMode(CORNER);
		translate(-30,0);
		stroke(255);
		for (int i=0;i<bounds.length;i++){
			Rect2D r=bounds[i];
			fill(i*255f/bounds.length,255,255);
			r.display(g);
		}
		
		
		noStroke();
		fill(0, 255, 255);
		//rectMode(CENTER);
		
		strokeCap(PROJECT);
		stroke(255);
		//colorMode(HSB);
		for (int i = 0; i < splits.size(); i++) {
			Split split = splits.get(i);
			strokeWeight(i * 0.02f);
			// stroke(i*10,255,255);
			for (int j = 0; j < split.hEdges.size(); j++) {
				FlibHEdge hE = split.hEdges.get(j);
				hE.display(g);
			}

		}
		//colorMode(RGB);
		
		

		for (int i = 0; i < plan.walls.size(); i++) {
			FlibWall wall = plan.walls.get(i);
			FlibHEdge hE = wall.hEdge;
			if (wall.type != null) {

				stroke(wall.type.color.getRGB());
			}
			line(hE.x1(), hE.y1(), hE.x2(), hE.y2());
			if (wall.type.label.equals("wall")) {
				noFill();
			} else {
				noFill();
			}
		}
		fill(255);
		//lights();
		//pointLight(200,200,200,30,30,10);
		noStroke();
		//roomRoot2.display(g);
		Vector<Room> splitLeaves2 = new Vector<Room>();
		roomRoot2.collectAllLeaves(splitLeaves2);
		for (int i = 0; i < splitLeaves2.size(); i++) {
			Room sA = splitLeaves2.get(i);
			
			//Room cid = splitLeaves.get(i);
			//sA.id=cid.id;
			//fill(cid.id*255f/bounds.length,255,255);
			//sA.display(g);
			FlibHEdge hE=sA.getHEdge();
			
			/*do{
				if (hE.getSym().getFace()!=null){
				beginShape(QUADS);
				vertex(hE.x1(),hE.y1(),0);
				vertex(hE.x2(),hE.y2(),0);
				vertex(hE.x2(),hE.y2(),3);
				vertex(hE.x1(),hE.y1(),3);
				endShape();
				}
				Vec2D[] footprint=this.calculateFootPrint(hE);
				for (int j = 0; j < footprint.length; j++) {
					HasPos2D n1 = footprint[j];
					HasPos2D n2 = footprint[(j + 1) % footprint.length];
					beginShape(QUAD);
					vertex(n1.getX(), n1.getY(), hE.z1());
					vertex(n2.getX(), n2.getY(), hE.z1());
					vertex(n2.getX(), n2.getY(), hE.z2() + 2.99f);
					vertex(n1.getX(), n1.getY(), hE.z2() + 2.99f);
					endShape();
				}
				hE=hE.getNext();
			}while(hE!=sA.getHEdge());*/

			// HasPos2D cP=sA.startP.p;
			// rect(cP.getX(),cP.getY(),0.5f,0.5f);
			//stroke(random(255), random(255), 0);
			// translate(random(-0.5f, 0.5f), random(-0.5f, 0.5f));
			// sA.display(g);
		}
		colorMode(RGB);
		stroke(0);
		strokeWeight(0.5f);
		for (int i2 = 0; i2 < splitLeaves2.size(); i2++) {
			Room sA = splitLeaves2.get(i2);
			FlibHEdge hE=sA.getHEdge();
			do{
				Room nb=(Room) hE.getSym().getFace();
				if (nb!=null&&sA!=null){
					if (connections[nb.id][sA.id]==0){
						hE.display(g);
					}
				}
				hE=hE.getNext();
			}while(hE!=sA.getHEdge());
			
		}
	}
	public void draw() {
		if (doReset){
			recreate();
			doReset=false;
		}
		background(0);

		Bounds3D b3D = plan.getBounds3D();
		
		//pushMatrix();
		stroke(0);
		strokeWeight(.1f);
		float scaleFactor = 10;
		scale(scaleFactor);
		translate(((-b3D.getCenterX() + width * 0.5f / scaleFactor)),
				((-b3D.getCenterY() + height * 0.5f / scaleFactor)));
		//translate(-b3D.getCenterX() ,-b3D.getCenterY());

		/*
		 * for (int i = 0; i < plan.rooms.size(); i++) { Room room =
		 * plan.rooms.get(i); if (room.ispublic) { fill(200); } fill(200);
		 * room.display(g); if (room.label != null) { fill(255, 0, 0); SVGText
		 * myText = room.label; this.textSize(0.3f); pushMatrix();
		 * translate(myText.x, myText.y); text(myText.text); popMatrix();
		 * point(myText.x, myText.y); } } stroke(255,255,0); for (int i = 0; i <
		 * plan.rooms.size(); i++) { Room room = plan.rooms.get(i); //Vec3D
		 * c=room.getGravityCenter(); float cX1=room.getBounds2D().getCenterX();
		 * float cY1=room.getBounds2D().getCenterY(); if (room.ispublic) {
		 * fill(200); } Vector<HEFace>nbs=room.getNbs(); for (int
		 * j=0;j<nbs.size();j++){ HEFace f=nbs.get(j); float
		 * cX=f.getBounds2D().getCenterX(); float
		 * cY=f.getBounds2D().getCenterY(); line(cX1,cY1,cX,cY); }
		 * 
		 * }
		 */
	
		//popMatrix();
		translate(-30,0);
		drawPlan();
		translate(30,0);
		drawPlan(roomRoot);
		translate(30,0);
		drawPlan(roomRoot2);
	}

	public void collectSplitFaces() {
		Vector<FlibHEdge> hEdges = plan.getAllHEdges();
		Vector<Room> areas = new Vector<Room>();
		roomRoot = null;
		Vector<FlibHEdge> allHEdges = new Vector<FlibHEdge>();
		for (int i = 0; i < hEdges.size(); i++) {
			FlibHEdge hE = hEdges.get(i);
			if (hE.getFace() == null) {
				FlibHEdge cHE = hE;
				do {
					allHEdges.add(cHE);
					cHE = cHE.getNext();
				} while (cHE != hE);

				break;
			}
		}

		for (int i = 0; i < hEdges.size(); i++) {
			FlibHEdge hE = hEdges.get(i);
			hE.detach();
		}
		roomRoot = new Room();
		//Collections.reverse(allHEdges);
		roomRoot.setPts(allHEdges);
		roomRoot.initCornerPts();
		roomRoot.setCornerPts();
		areas.add(roomRoot);

		// println("size: " + root.getPts().size());
		fill(0, 255, 0);
		// area1.display(g);
		stroke(0, 255, 255);
		while (areas.size() > 0) {
			Vector<Room> newAreas = new Vector<Room>();
			for (int j = 0; j < areas.size(); j++) {
				Room area = areas.get(j);
				if (area.children.size()==0) {
					for (int i = 0; i < splits.size(); i++) {
						Split split = splits.get(i);
						if (area.contains(split)) {
							Vector<Room> splitAreas = area.split(split);
							newAreas.addAll(splitAreas);		
							break;
						}
					}
				}
			}
			areas = newAreas;
		}
		Vector<Room> splitLeaves = new Vector<Room>();
		roomRoot.collectAllLeaves(splitLeaves);

		for (int i = 0; i < splitLeaves.size(); i++) {
			Room sA = splitLeaves.get(i);
			Rect2D cB=sA.getBounds2D();
			for (int j = 0; j < plan.rooms.size(); j++) {
				Room r=plan.rooms.get(j);
				
				if (bounds[r.id].getOverlapArea(cB)>0.2f){
					sA.id=r.id;
					
				}
				
			}
				
		}
	}

	public void collectAxis() {
		Vector<FlibHEdge> hEdges = plan.getAllHEdges();
		splits = new Vector<Split>();
		for (int i = 0; i < hEdges.size(); i++) {
			FlibHEdge hE = hEdges.get(i);
			FlibHEData heData = (FlibHEData) hE.getData();
			if (hE.getFace() != null && hE.getSym().getFace() != null) {
				if (heData.nextAxisEdge == null && heData.split == null) {
					Split split = new Split();
					splits.add(split);
					FlibHEdge symHE = hE.getSym();
					do {
						FlibHEData symHEData = (FlibHEData) symHE.getData();
						symHEData.split = split;
						FlibHEData symHEData2 = (FlibHEData) symHE.getSym().getData();
						symHEData2.split = split;
						split.hEdges.add(symHE);
						symHE = symHEData.nextAxisEdge;
					} while (symHE != null);
				}
			}
		}
		Collections.sort(splits);

	}

	public void findSplits(FlibHEdge hEdge) {

		FlibHEData heData = (FlibHEData) hEdge.getData();
		if (heData.nextAxisEdge == null) {
			FlibHEdge start = hEdge.getSym();
			FlibHEdge sym = start;
			float cAngle1 = (float) hEdge.getAngle();
			FlibHEdge nextAxis = null;
			float deltaAxis = 180;
			do {
				float deltaAngle = Vec2D.getAngleBetween(start.getDest(),
						start.getOrg(), sym.getDest());
				if (abs(deltaAngle - PI) < deltaAxis) {
					deltaAxis = abs(deltaAngle - PI);
					nextAxis = sym;
				}
				sym = sym.getNextO();
			} while (sym != hEdge.getSym());

			if (deltaAxis < 0.2f) {
				heData.nextAxisEdge = nextAxis;
				((FlibHEData) nextAxis.getSym().getData()).nextAxisEdge = hEdge
						.getSym();
			}
		}
	}

	public void findSplits() {

		for (int i = 0; i < plan.walls.size(); i++) {
			FlibWall wall = plan.walls.get(i);
			FlibHEdge hEdge = wall.hEdge;
			if (hEdge.getFace() != null && hEdge.getSym().getFace() != null) {
				findSplits(hEdge);
				findSplits(hEdge.getSym());
			}
		}

	}
	
	public void keyPressed(){
		doReset=true;
	}
 public Vec2D[] calculateFootPrint(FlibHEdge hEdge){
	 Vec2D[] footprint=new Vec2D[6];
	 float of1=0.2f;
	 float of2=0.2f;
	/* if (getR1()==null){
		 of1=0.2f;
	 }
	 if (getR2()==null){
		 of2=0.2f;
	 }*/
	 Vec2D[] o1=getOffset(hEdge,of1,of1,of1);
	 Vec2D[] o2=getOffset(hEdge.getSym(),of2,of2,of2);
	 footprint[0]=new Vec2D(hEdge.getX1(),hEdge.getY1());
	 footprint[1]=o1[0];
	 footprint[2]=o1[1];
	 footprint[3]=new Vec2D(hEdge.getX2(),hEdge.getY2());
	 footprint[4]=o2[0];
	 footprint[5]=o2[1];
	 return footprint;
}
 public Vec2D[] getOffset(FlibHEdge hEdge,float o0,float o1,float o2){
	 Vec2D[] offsetPts=new Vec2D[2];
	 FlibHEdge nextHE=hEdge.getNext();
	 FlibHEdge prevHE=hEdge.getPrev();
	 Edge e=Vec3D.getOffsetEdge(hEdge.getOrg(), hEdge.getDest(), o1);
	 offsetPts[0]=Vec3D.getOffsetPoint(prevHE.getOrg(),hEdge.getOrg(), hEdge.getDest(), o0, o1);
	 offsetPts[1]=Vec3D.getOffsetPoint(hEdge.getOrg(), hEdge.getDest(), nextHE.getDest(),o1, o2);
	 if (offsetPts[0]==null||Vec2D.dist(offsetPts[0],hEdge.getOrg())>1)
		 offsetPts[0]=(Vec2D) e.getP1();
	 if (offsetPts[1]==null||Vec2D.dist(offsetPts[1],hEdge.getDest())>1)
		 offsetPts[1]=(Vec2D) e.getP2();
	 return offsetPts;
 }
}